/**
 ******************************************************************************
 * @file           : bsp_iwdg.c
 * @brief          : IWDG Driver Implementation (STM32F1xx)
 ******************************************************************************
 * @attention
 *
 * 独立看门狗（IWDG）驱动实现文件
 * 提供系统级故障保护功能
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "./BSP/IWDG/bsp_iwdg.h"
#include "./BSP/IWDG/bsp_iwdg_config.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/

static IWDG_HandleTypeDef sg_iwdg_handle;       /* 看门狗句柄 */
static uint8_t sg_iwdg_enabled = 0;             /* 看门狗启用标志 */
static uint16_t sg_iwdg_timeout_ms = 0;         /* 看门狗超时时间（ms） */

#if IWDG_MONITOR_ENABLE
static uint32_t sg_last_feed_time = 0;          /* 上次喂狗时间戳（ms） */
static uint32_t sg_last_feed_interval = 0;      /* 上次喂狗间隔时间（ms） */
#endif

/* Private function prototypes -----------------------------------------------*/

static uint8_t calculate_reload_value(uint16_t timeout_ms, uint32_t *prescaler, uint16_t *reload);

/* Private functions ---------------------------------------------------------*/

/**
 * @brief       计算看门狗重载值和分频系数
 *
 * @param[in]   timeout_ms: 期望的超时时间（毫秒）
 * @param[out]  prescaler: 计算得到的分频系数
 * @param[out]  reload: 计算得到的重载值
 *
 * @retval      1: 成功
 * @retval      0: 失败（参数无效或超出范围）
 *
 * @note        IWDG超时时间 = (prescaler / 40000) * reload * 1000 ms
 * @note        有效范围：约0.1ms ~ 26214ms
 */
static uint8_t calculate_reload_value(uint16_t timeout_ms, uint32_t *prescaler, uint16_t *reload)
{
    /* 参数检查 */
    if (prescaler == NULL || reload == NULL || timeout_ms == 0)
    {
        return 0;
    }
    
    /* 分频系数数组 */
    const uint32_t prescaler_values[] = 
    {
        IWDG_PRESCALER_4,       /* 分频4 */
        IWDG_PRESCALER_8,       /* 分频8 */
        IWDG_PRESCALER_16,      /* 分频16 */
        IWDG_PRESCALER_32,      /* 分频32 */
        IWDG_PRESCALER_64,      /* 分频64 */
        IWDG_PRESCALER_128,     /* 分频128 */
        IWDG_PRESCALER_256      /* 分频256 */
    };
    const uint16_t prescaler_divisors[] = {4, 8, 16, 32, 64, 128, 256};
    
    /* 尝试找到合适的分频系数和重载值 */
    for (uint8_t i = 0; i < 7; i++)
    {
        /* 计算重载值：reload = (timeout_ms * IWDG_CLOCK_FREQ) / (prescaler * 1000) */
        uint32_t calculated_reload = ((uint32_t)timeout_ms * IWDG_CLOCK_FREQ) / (prescaler_divisors[i] * 1000);
        
        /* 检查是否在有效范围内 */
        if (calculated_reload > 0 && calculated_reload <= IWDG_RELOAD_MAX)
        {
            *prescaler = prescaler_values[i];
            *reload = (uint16_t)calculated_reload;
            return 1;
        }
    }
    
    /* 超出范围 */
    return 0;
}

/* Exported functions --------------------------------------------------------*/

/**
 * @brief       初始化看门狗（指定超时时间）
 *
 * @param[in]   timeout_ms: 超时时间（毫秒），有效范围约0.1ms ~ 26214ms
 *
 * @retval      无
 *
 * @note        看门狗一旦启动，只能通过系统复位关闭
 * @note        建议超时时间：1000ms（1秒）
 * @note        喂狗间隔应为超时时间的50%，例如：1秒超时，500ms喂一次狗
 * @note        如果参数无效，函数将使用默认配置（1秒超时）
 */
void iwdg_init(uint16_t timeout_ms)
{
    uint32_t prescaler;
    uint16_t reload;
    
    /* 计算分频系数和重载值 */
    if (calculate_reload_value(timeout_ms, &prescaler, &reload) == 0)
    {
        /* 参数无效，使用默认配置：1秒超时 */
        prescaler = IWDG_PRESCALER_64;
        reload = 625;
        sg_iwdg_timeout_ms = 1000;
    }
    else
    {
        /* 计算并保存实际超时时间 */
        /* prescaler 值：0=4, 1=8, 2=16, 3=32, 4=64, 5=128, 6=256 */
        uint32_t prescaler_divisor = 4 << (prescaler & 0x07);
        sg_iwdg_timeout_ms = (uint16_t)((prescaler_divisor * reload * 1000) / IWDG_CLOCK_FREQ);
    }
    
    /* 配置看门狗 */
    sg_iwdg_handle.Instance = IWDG;
    sg_iwdg_handle.Init.Prescaler = prescaler;
    sg_iwdg_handle.Init.Reload = reload;
    
    /* 初始化看门狗 */
    if (HAL_IWDG_Init(&sg_iwdg_handle) != HAL_OK)
    {
        log_e("看门狗初始化失败！");
        return;
    }
    
    /* 标记已启用 */
    sg_iwdg_enabled = 1;
    
    /* 打印初始化信息 */
    log_i("看门狗已启用，超时时间: %d ms", sg_iwdg_timeout_ms);
    
#if IWDG_MONITOR_ENABLE
    /* 初始化监控变量 */
    sg_last_feed_time = HAL_GetTick();
    sg_last_feed_interval = 0;
    log_i("看门狗监控已启用，预期喂狗间隔: %d ms，警告阈值: %d ms", 
          IWDG_EXPECTED_FEED_INTERVAL, IWDG_FEED_WARNING_THRESHOLD);
#endif
}

/**
 * @brief       初始化看门狗（自定义配置）
 *
 * @param[in]   prescaler: 分频系数
 *                         - IWDG_PRESCALER_4   (分频4)
 *                         - IWDG_PRESCALER_8   (分频8)
 *                         - IWDG_PRESCALER_16  (分频16)
 *                         - IWDG_PRESCALER_32  (分频32)
 *                         - IWDG_PRESCALER_64  (分频64)
 *                         - IWDG_PRESCALER_128 (分频128)
 *                         - IWDG_PRESCALER_256 (分频256)
 * @param[in]   reload: 重载值（0-4095）
 *
 * @retval      1: 初始化成功
 * @retval      0: 初始化失败（参数无效）
 *
 * @note        超时时间(ms) = (prescaler / 40000) * reload * 1000
 * @note        例如：prescaler=64, reload=625 => 超时时间 = 1000ms
 */
uint8_t iwdg_init_ex(uint32_t prescaler, uint16_t reload)
{
    /* 参数检查 */
    if (reload > IWDG_RELOAD_MAX)
    {
        return 0;
    }
    
    /* 检查分频系数是否有效 */
    if (prescaler != IWDG_PRESCALER_4 && 
        prescaler != IWDG_PRESCALER_8 && 
        prescaler != IWDG_PRESCALER_16 && 
        prescaler != IWDG_PRESCALER_32 && 
        prescaler != IWDG_PRESCALER_64 && 
        prescaler != IWDG_PRESCALER_128 && 
        prescaler != IWDG_PRESCALER_256)
    {
        return 0;
    }
    
    /* 配置看门狗 */
    sg_iwdg_handle.Instance = IWDG;
    sg_iwdg_handle.Init.Prescaler = prescaler;
    sg_iwdg_handle.Init.Reload = reload;
    
    /* 初始化看门狗 */
    if (HAL_IWDG_Init(&sg_iwdg_handle) != HAL_OK)
    {
        return 0;
    }
    
    /* 计算并保存实际超时时间 */
    /* prescaler 值：0=4, 1=8, 2=16, 3=32, 4=64, 5=128, 6=256 */
    uint32_t prescaler_divisor = 4 << (prescaler & 0x07);
    sg_iwdg_timeout_ms = (uint16_t)((prescaler_divisor * reload * 1000) / IWDG_CLOCK_FREQ);
    
    /* 标记已启用 */
    sg_iwdg_enabled = 1;
    
    return 1;
}

/**
 * @brief       喂狗（重载计数器）
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        必须在超时之前调用此函数，否则系统将复位
 * @note        建议喂狗间隔为超时时间的50%
 * @note        此函数非常轻量，可在任务中频繁调用
 */
void iwdg_feed(void)
{
#if IWDG_MONITOR_ENABLE
    /* 记录喂狗时间和间隔 */
    uint32_t current_time = HAL_GetTick();
    sg_last_feed_interval = current_time - sg_last_feed_time;
    sg_last_feed_time = current_time;
#endif
    
    /* 喂狗 */
    HAL_IWDG_Refresh(&sg_iwdg_handle);
}

/**
 * @brief       检查看门狗是否已启用
 *
 * @param       无
 *
 * @retval      1: 看门狗已启用
 * @retval      0: 看门狗未启用
 *
 * @note        看门狗一旦启用，无法通过软件关闭，只能通过系统复位
 */
uint8_t iwdg_is_enabled(void)
{
    return sg_iwdg_enabled;
}

/**
 * @brief       获取当前超时时间
 *
 * @param       无
 *
 * @retval      当前超时时间（毫秒）
 *
 * @note        返回初始化时配置的超时时间
 */
uint16_t iwdg_get_timeout(void)
{
    return sg_iwdg_timeout_ms;
}

/**
 * @brief       获取上次喂狗间隔时间
 *
 * @param       无
 *
 * @retval      上次喂狗间隔时间（毫秒）
 *
 * @note        只有启用IWDG_MONITOR_ENABLE时才有效
 * @note        返回从上上次喂狗到上次喂狗的时间间隔
 */
uint32_t iwdg_get_last_feed_interval(void)
{
#if IWDG_MONITOR_ENABLE
    return sg_last_feed_interval;
#else
    return 0;
#endif
}

/**
 * @brief       检查喂狗间隔（超过阈值打印警告）
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        只有启用IWDG_MONITOR_ENABLE时才有效
 * @note        如果喂狗间隔超过IWDG_FEED_WARNING_THRESHOLD，打印警告日志
 * @note        建议在WatchdogTask任务中定期调用此函数
 */
void iwdg_check_feed_interval(void)
{
#if IWDG_MONITOR_ENABLE
    /* 跳过第一次检查（初始化时sg_last_feed_interval为0） */
    if (sg_last_feed_interval == 0)
    {
        return;
    }
    
    /* 检查是否超过警告阈值 */
    if (sg_last_feed_interval > IWDG_FEED_WARNING_THRESHOLD)
    {
        log_w("喂狗间隔异常: %lu ms (预期%dms)", 
              (unsigned long)sg_last_feed_interval, 
              IWDG_EXPECTED_FEED_INTERVAL);
    }
#endif
}
